/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.webos.java.base;

import java.util.ArrayList;
import java.util.List;
import org.json.JSONObject;
import org.urchr.web.db.DBSession;
import org.webos.java.base.db.DBWorkspace;
import org.webos.java.base.db.User;
import org.webos.java.base.workspace.FrameHolder;
import org.webos.java.base.workspace.Splitter;
import org.webos.java.base.workspace.Workspace;
import org.webos.java.base.workspace.WorkspaceItem;
import org.webos.java.root.Log;

/**
 * 
 * @author kostya
 */

@ApplicationType(name="Core", code="core", description="Core application", jsClass="core.app.Application", systemType=true)
public class CoreApplication extends Application{

    private List<Workspace> wspaceList = new ArrayList<Workspace>();


    public CoreApplication(Session aSession) {
        super(aSession);
//        try {
//            Log.debug(this, "----RSS test start");
//            SystemManager.setupProxy(aSession);
//            URL feedUrl = new URL("http://bash.org.ru/rss/");
//            SyndFeedInput input = new SyndFeedInput();
//            SyndFeed feed = input.build(new InputStreamReader(feedUrl.openStream()));
//            List<SyndEntryImpl> entries = feed.getEntries();
//            Log.debug(this, "Feed: "+feed.getDescription(), "entries:", entries.size());
//            for (SyndEntryImpl entry : entries) {
//                Log.debug("Entry: ", entry.getTitle(), entry.getPublishedDate(),
//                        entry.getLink(), entry.getUri(), entry.getDescription().getMode(),
//                        entry.getDescription().getType(), entry.getDescription().getValue());
//            }
////            System.out.println("Feed: "+feed);
//            Log.debug(this, "----RSS test end");
//        } catch (Throwable t) {
//            Log.error(this, "Exception thrown: " + t.getMessage());
//            t.printStackTrace();
//        }

    }

    protected void saveWorkspacetoDB(Workspace aWorkspace){
        DBSession dbs = new DBSession();
        try {
            dbs.begin();
//            DBApplication app = getApplication(dbs);
            User user = getSession().getUser(dbs);
            DBWorkspace dbw = (DBWorkspace) dbs.createQuery("from DBWorkspace where name=:name and user=:user").
                    setString("name", aWorkspace.getCode()).setEntity("user", user).
                    uniqueResult();
            if(dbw==null){
                dbw = new DBWorkspace();
                dbw.setName(aWorkspace.getCode());
                dbw.setUser(user);
            }
            dbw.setValue(workspaceToJSON(aWorkspace).toString());
            dbs.save(dbw);
            dbs.commit();
        } catch (Throwable t) {
            dbs.rollback();
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        } finally {
            dbs.close();
        }
    }

    void registerWorkspace(Workspace aWorkspace){
        for (Workspace workspace : wspaceList) {
            if(workspace.getCode().equals(aWorkspace.getCode())){
                workspace.setApplication(aWorkspace.getApplication());
                return;
            }
        }
        //Convert to JSON, restore from JSON, comape
        //First, check in db
        DBSession dbs = new DBSession();
        try {
            User user = getSession().getUser(dbs);
            DBWorkspace dbw = (DBWorkspace) dbs.createQuery("from DBWorkspace where name=:name and user=:user").
                    setString("name", aWorkspace.getCode()).setEntity("user", user).
                    uniqueResult();
            if(dbw!=null){
                Workspace ws = jSONToWorkspace(new JSONObject(dbw.getValue()));
                ws.setApplication(aWorkspace.getApplication());
                wspaceList.add(ws);
                return;
            }
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        } finally {
            dbs.close();
        }
        Workspace cloned = cloneWorkspace(aWorkspace);
        wspaceList.add(cloned);
        saveWorkspacetoDB(cloned);
    }

    private WorkspaceItem getWorkspaceItem(WorkspaceItem aItem, String aID){
        if(aItem.getCode().equals(aID))
            return aItem;
        if(aItem instanceof Splitter){
            Splitter splitter = (Splitter) aItem;
            if(splitter.getLeftItem()!=null){
                WorkspaceItem item = getWorkspaceItem(splitter.getLeftItem(), aID);
                if(item!=null)
                    return item;
            }
            if(splitter.getRightItem()!=null){
                WorkspaceItem item = getWorkspaceItem(splitter.getRightItem(), aID);
                if(item!=null)
                    return item;
            }
        }
        return null;
    }

    private void moveDivider(WorkspaceItem aItem, String aID, double aRatio){
        WorkspaceItem item = getWorkspaceItem(aItem, aID);
        if(item!=null && item instanceof Splitter){
            Splitter splitter = (Splitter) item;
            splitter.setRatio(aRatio);
        }
    }

    private Workspace getCachedWorkspace(String aName){
        for (Workspace workspace : wspaceList) {
            if(workspace.getCode().equals(aName))
                return workspace;
        }
        return null;
    }

    protected void sendAddWorkspace(Workspace aWorkspace){
        sendMessage("addWorkspace", workspaceToJSON(aWorkspace));
    }

    @RemoteMessage
    public void showApplication(JSONObject aObject){
        try {
            String app = aObject.optString("app", "");
            if(app.equals("")){
                getSession().announceApplications();
            }else{
                for (Application application : getSession().applications) {
                    if(application.getCode().equals(app)){
                        getSession().announceApplication(application);
                    }
                }
            }
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
    }

    @RemoteMessage
    public void resetWorkspace(JSONObject aONObject){
        try {
            String workspaceName = aONObject.optString("workspace", "");
            Log.debug(this, "Reset workspace: "+workspaceName);
            for (int i = 0; i < wspaceList.size(); i++) {
                Workspace ws = wspaceList.get(i);
                if(ws.getCode().equals(workspaceName) && ws.getApplication()!=null){
                    //We found modified workspace
                    for (int j = 0; j < ws.getApplication().getWorkspacesCount(); j++) {
                        Workspace ws2 = ws.getApplication().getWorkspaceAt(j);
                        if(ws2.getCode().equals(workspaceName)){
                            //We found original
                            wspaceList.remove(ws);
                            JSONObject obj = workspaceToJSON(ws2);
//                            Log.debug(this, "Original ws: "+obj);
                            Workspace cloned = jSONToWorkspace(obj);
                            cloned.setApplication(ws.getApplication());
                            //check all dynamic frames and add them to correct place
                            for (int k = 0; k < ws.getApplication().getFramesCount(); k++) {
                                Frame frame = ws.getApplication().getFrameAt(k);
                                if(frame.isDynamic() && frame.getFrameHolder()!=null){
                                    FrameHolder fh = (FrameHolder) getWorkspaceItem(cloned.getItem(),
                                            frame.getFrameHolder().getCode());
                                    if(fh!=null)
                                        fh.addFrame(frame);
                                }

                            }
                            wspaceList.add(cloned);
                            obj.put("selected", "true");
                            getSession().getCoreApplication().sendMessage("resetWorkspace", workspaceToJSON(cloned));
                            saveWorkspacetoDB(cloned);
                            return;
                        }
                    }
                }
            }
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
    }

    @RemoteMessage
    public void workspaceSelected(JSONObject aObject){
        Workspace ws = getCachedWorkspace(aObject.optString("workspace"));
        if(ws!=null && ws.getType()==Workspace.TYPE_FLOAT)
            return;
        for (Workspace workspace : wspaceList) {
            if(workspace.getCode().equals(aObject.optString("workspace"))){
                workspace.setSelected(true);
            }else{
                workspace.setSelected(false);
            }
            if(workspace.getApplication()!=null)
                workspace.getApplication().workspaceStateChanged(workspace);
        }
    }

    @RemoteMessage
    public void workspaceClosing(JSONObject aObject){
//        Log.debug(this, "Ask about workspace");
        Workspace ws = getCachedWorkspace(aObject.optString("workspace"));
        if(ws!=null){
            ws.getApplication().requestCloseWorkspace(ws);
        }
    }

    @RemoteMessage
    public void frameAdded(JSONObject aObject){
//        Log.debug(this, "Removing holder", aObject.optString("holder"));
        try {
            Workspace ws = getCachedWorkspace(aObject.optString("workspace"));
            if(ws!=null){
                WorkspaceItem item = getWorkspaceItem(ws.getItem(), aObject.optString("holder"));
                if(item!=null && (item instanceof FrameHolder)){
                    FrameHolder fh = (FrameHolder) item;
                    if(!fh.getFrameCodes().contains(aObject.optString("frame"))){
                        fh.getFrameCodes().add(aObject.optString("frame"));
                        saveWorkspacetoDB(ws);
                    }
                }
            }
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
    }

    @RemoteMessage(name="frameSelected")
    public void frameSelected(JSONObject aObject){
        try {
            Workspace ws = getCachedWorkspace(aObject.optString("workspace"));
            if(ws!=null){
                WorkspaceItem item = getWorkspaceItem(ws.getItem(), aObject.optString("holder"));
                if(item!=null && (item instanceof FrameHolder)){
                    FrameHolder fh = (FrameHolder) item;
                    fh.setActiveFrame(aObject.optString("frame"));
                }
            }
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
    }

    @RemoteMessage(name="frameRemoved")
    public void frameRemoved(JSONObject aObject){
        Log.debug(this, "Removing frame from",
                aObject.optString("holder"),
                aObject.optString("workspace"),
                aObject.optString("frame"));
        try {
            Workspace ws = getCachedWorkspace(aObject.optString("workspace"));
            if(ws!=null){
                WorkspaceItem item = getWorkspaceItem(ws.getItem(), aObject.optString("holder"));
                if(item!=null && (item instanceof FrameHolder)){
                    FrameHolder fh = (FrameHolder) item;
                        fh.getFrameCodes().remove(aObject.optString("frame"));
                        saveWorkspacetoDB(ws);
                }
            }
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
    }

    @RemoteMessage(name="holderRemoved")
    public void holderRemoved(JSONObject aObject){
//        Log.debug(this, "Removing holder", aObject.optString("holder"), "from ws", aObject.optString("workspace"));
        try {
            for (Workspace workspace : wspaceList) {
//                Log.debug(this, "compare", workspace.getCode(), "with", aObject.optString("workspace"));
                if(workspace.getCode().equals(aObject.optString("workspace"))){
                    WorkspaceItem item = getWorkspaceItem(workspace.getItem(),
                            aObject.optString("holder"));
//                    Log.debug(this, "wsItem", item);
                    if(item!=null && item.getParent()!=null && (item instanceof FrameHolder)){
//                        Log.debug(this, "Found FrameHolder");
                        FrameHolder fh = (FrameHolder) item;
                        Splitter parent = (Splitter) fh.getParent();
                        WorkspaceItem neighbour = parent.getLeftItem();
                        if(neighbour.equals(item))
                            neighbour = parent.getRightItem();
                        if(parent.getParent()!=null){
                            Splitter pparent = (Splitter) parent.getParent();
                            if(pparent.getLeftItem().equals(parent)){
                                pparent.setLeftItem(neighbour);
                            }else{
                                pparent.setRightItem(neighbour);
                            }
                        }else{
                            workspace.setItem(neighbour);
                        }
                        neighbour.setParent(parent.getParent());
                        saveWorkspacetoDB(workspace);
                        break;
                    }
                }

            }
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }

    }

    @RemoteMessage(name="holderSplitted")
    public void holderSplitted(JSONObject aObject){
        try {
            Log.debug(this, "Look for ws", aObject.optString("workspace"),
                    aObject.optString("splitterID"), 
                    aObject.optString("holderID"),
                    aObject.optString("holder"));
            for (Workspace workspace : wspaceList) {
                if(workspace.getCode().equals(aObject.optString("workspace"))){
                    WorkspaceItem item = getWorkspaceItem(workspace.getItem(),
                            aObject.optString("holder"));
                    if(item!=null && (item instanceof FrameHolder)){
                        Log.debug(this, "Found FrameHolder");
                        FrameHolder fh = (FrameHolder) item;
                        Splitter sp = new Splitter(fh.getParent(), aObject.getString("splitterID"));
                        FrameHolder empty = new FrameHolder(sp, aObject.getString("holderID"));
                        sp.setVertical(aObject.getBoolean("vertical"));
                        if(!aObject.getBoolean("right")){
                            sp.setLeftItem(item);
                            sp.setRightItem(empty);
                        }else{
                            sp.setRightItem(item);
                            sp.setLeftItem(empty);
                        }
                        if(item.getParent()==null){
                            workspace.setItem(sp);
                        }else{
                            Splitter parent = (Splitter) item.getParent();
                            if(parent.getLeftItem().equals(item))
                                parent.setLeftItem(sp);
                            else
                                parent.setRightItem(sp);
                        }
                        item.setParent(sp);
                    }
                    saveWorkspacetoDB(workspace);
                    break;
                }
            }
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
    }


    @RemoteMessage
    public void workspaceResized(JSONObject aObject){
        try {
            Workspace workspace = getCachedWorkspace(aObject.optString("workspace"));
            if(workspace!=null && workspace.getType()==Workspace.TYPE_FLOAT){
                workspace.setWidth(aObject.optInt("width", 300));
                workspace.setHeight(aObject.optInt("height", 300));
                saveWorkspacetoDB(workspace);
            }
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
    }

    @RemoteMessage
    public void workspaceRaized(JSONObject aObject){
        try {
            for (Workspace workspace : wspaceList) {
                if(workspace.getCode().equals(aObject.opt("workspace")) && workspace.getType()==Workspace.TYPE_FLOAT){
                    workspace.setCollapsed(false);
  //                  saveWorkspacetoDB(workspace);
                    break;
                }
            }
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
    }

    @RemoteMessage
    public void workspaceHidden(JSONObject aObject){
        try {
            for (Workspace workspace : wspaceList) {
                if(workspace.getCode().equals(aObject.opt("workspace")) && workspace.getType()==Workspace.TYPE_FLOAT){
                    workspace.setCollapsed(true);
//                    saveWorkspacetoDB(workspace);
                    break;
                }
            }
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
    }

    @RemoteMessage(name="dividerMoved")
    public void dividerMoved(JSONObject aObject){
        try {
            for (Workspace workspace : wspaceList) {
                if(workspace.getCode().equals(aObject.opt("workspace"))){
                    moveDivider(workspace.getItem(), aObject.optString("name"), aObject.getDouble("divider"));
                    saveWorkspacetoDB(workspace);
                    break;
                }
            }
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
    }

    Workspace getCachedWorkspace(Workspace aWorkspace){
        for (Workspace workspace : wspaceList) {
            if(workspace.getCode().equals(aWorkspace.getCode()))
                return workspace;
        }
        return aWorkspace;
    }

    @Override
    protected void requestCloseWorkspace(Workspace aWorkspace) {
        //Do nothing
    }


}
